#ifndef VL_STREAM_HPP
#define VL_STREAM_HPP

#include <stdexcept>
#include <cstring>
#include <string>
#include "common.hpp"
#include "debug.hpp"
#include "shared_ptr.hpp"
#if defined(ENABLE_PCL)
#include "pcl/pcl.hpp"
#endif

namespace vl
{
	
struct buffer
{
	buffer();
	
	buffer(byte const* p, size_t s);

	~buffer();
	
	void clear();
	
	void resize(size_t newSize);
	
	size_t size() const;
	
	bool empty() const;
	
	void insert(byte const* p, size_t s);
	
	void insert(byte b, size_t s);
		
	void consume(size_t amount);
	
	byte& operator[](size_t idx);
	
	byte const& operator[](size_t idx) const;
	
	void _invariants() const;
	
	// The real buffer is in [_buffer_base, _buffer_base + _buffer_size)
	byte*  _buffer_base;
	size_t _buffer_size;
	byte*  _buffer_limit;
	byte*  _buffer;
};

#if 0 // TODO
struct immutable_buffer
{
	struct holder : shared
	{
		holder(buffer& src)
		, _buffer_end(src._buffer_base + src._buffer_size)
		, _buffer(src._buffer)
		{
			
		}
		
		~holder()
		{
			std::free(_buffer);
		}
		
		byte* _buffer_end;
		byte* _buffer;
	};
	
	immutable_buffer(buffer& src)
	: _content(new holder(src))
	, _buffer_base(src._buffer_base)
	{
		src._buffer_base = 0;
		src._buffer_size = 0;
		src._buffer_limit = 0;
		src._buffer = 0;
	}
	
	// default copy-ctor and op= is fine
	
	std::size_t size()
	{
		return _content ? _content->_buffer_end - _buffer_base : 0;
	}
	
	byte& operator[](std::size_t idx);
	{
		ASSERT(!_content || idx < size());
		return _buffer_base[idx];
	}
	
	void consume(std::size_t amount)
	{
		_buffer_base += amount;
	}
	
	shared_ptr<holder> _content;
	byte*  _buffer_base;
};
#endif


struct device_buf // : public source_buf, public sink_buf
{
	static size_t const read_buffer_size = 1024;
	static size_t const eof_value = std::size_t(-1);
	
	device_buf();
	
	device_buf(byte const* p, std::size_t s);
	
	virtual ~device_buf();
	
	// Returns a pointer to a chunk //size// bytes large that is in the buffer.
	// The caller should fill this with data that it wants written to the stream.
	// NOTE: The pointer is valid until the next call to flush() / write_amount() / write_buffer()
	virtual byte* write_buffer(std::size_t size);
	
	// Writes the bytes in [p, p + size) to the stream
	virtual void write_amount(byte const* p, std::size_t size);
	
	// Tries to write the buffered data to the stream
	bool flush();
	
	
	
	virtual void do_close();

	void close();
	
	buffer write_buf;
	
	
	// Returns the current buffer size
	size_t read_size() const;
	
	// Tries to buffer //size// bytes
	bool read_amount(size_t size);
	
	bool eof() const;

	bool try_read_amount(std::size_t size);
	
	void read_pad(std::size_t size);
	
	void read_direct_fill(byte const* p, std::size_t size);
	
	// Returns a pointer to a buffer read_size() long
	byte const* read_buffer() const;
	
	// Consumes the first //size// bytes in the buffer
	void read_consume(std::size_t size);
	
//protected:
	virtual size_t write(byte const* p, std::size_t size) = 0;
	virtual size_t read(byte* p, std::size_t size) = 0;
	virtual bool wait_for_data(int msec)
	{
		// TODO: Sleep for msec
		return true;
	}
		
	virtual int error()
	{
		return 0;
	}

	buffer read_buf;
	std::size_t write_buffer_size;
	void (*blocking)(device_buf*); // Function to be called when a read request fails because of underflow
	bool _reached_eof;
};

struct memory_buf : public device_buf
{
	memory_buf();
	
	memory_buf(byte const* p, size_t s);
	
	virtual byte* write_buffer(size_t size);
	
	virtual void write_amount(byte const* p, size_t size);
	
	virtual size_t read(byte* p, size_t size);

	virtual size_t write(byte const* p, size_t size);
};

struct iobase
{
	iobase()
	: dev(0)
	{
	}
	
	iobase(device_buf* dev)
	: dev(dev)
	{
	}
	
	void devbuf(device_buf* dev_new)
	{
		delete dev;
		dev = dev_new;
	}
	
	device_buf* release_devbuf()
	{
		device_buf* res = dev;
		dev = 0;
		return res;
	}
	
	device_buf* get_devbuf()
	{
		return dev;
	}
	
	void set_blocking(void (*blocking)(device_buf*))
	{
		ASSERT(dev);
		dev->blocking = blocking;
	}
	
#if defined(ENABLE_PCL)
	static void coro_blocking(device_buf*)
	{
		pcl::resume();
	}
	
	void set_coro_blocking()
	{
		dev->blocking = coro_blocking;
	}
#endif
	
	int error()
	{
		ASSERT(dev);
		return dev->error();
	}
	
	operator void*()
	{
		return (dev && !error() && !dev->eof()) ? dev : 0;
	}
	
	void close()
	{
		if(dev)
			dev->close();
	}
	
	virtual ~iobase()
	{
		close();
		delete dev;
	}

protected:
	device_buf* dev;
};

struct istream : virtual iobase
{
	istream()
	: _consumed(0)
	{
	}
	
	size_t read_size()
	{
		return dev->read_size();
	}
	
	// Tries to buffer //size// bytes
	bool read(size_t size)
	{
		return dev->read_amount(size);
	}
	
	/*
	vl::byte* read(std::size_t size)
	{
		if(dev->read_amount(size))
			return dev->read_buffer();
		return 0;
	}*/
	
	bool eof()
	{
		return dev->eof();
	}
	
	// Tries to add //size// more bytes to the buffer
	
	bool try_read(std::size_t size)
	{
		return dev->try_read_amount(size);
	}
	
	void read_pad(std::size_t size)
	{
		return dev->read_pad(size);
	}
	
	// Returns a pointer to a buffer read_size() long
	byte const* read_buffer() const
	{
		return dev->read_buffer();
	}
	
	// Consumes the first //size// bytes in the buffer.
	// All consumption must go through this function.
	void read_consume(std::size_t size)
	{
		_consumed += size;
		dev->read_consume(size);
	}
	
	bool read_aint(unsigned int& v)
	{
		read(5);
		
		std::size_t max = read_size();
		vl::byte const* p = read_buffer();
		
		v = 0;
		
		for(std::size_t i = 0; i < max; ++i)
		{
			vl::byte b = p[i];
			
			if((b & 0x80) == 0)
			{
				v |= b;
				return true;
			}
			else
			{
				v = (v << 7) | (b & 0x7f);
			}
		}
		
		return false;
	}
	
	bool read_int32(unsigned int& dest)
	{
		if(!read(4))
			return false;
		byte const* buf = read_buffer();
		dest = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
		read_consume(4);
		return true;
	}
	
	bool read_int24(unsigned int& dest)
	{
		if(!read(3))
			return false;
		byte const* buf = read_buffer();
		dest = buf[0] | (buf[1] << 8) | (buf[2] << 16);
		read_consume(3);
		return true;
	}
	
	bool read_int16(unsigned int& dest)
	{
		if(!read(2))
			return false;
		byte const* buf = read_buffer();
		dest = buf[0] | (buf[1] << 8);
		read_consume(2);
		return true;
	}
	
	bool read_int8(unsigned int& dest)
	{
		if(!read(1))
			return false;
		byte const* buf = read_buffer();
		dest = buf[0];
		read_consume(1);
		return true;
	}
	
	bool read_sint16(int& dest)
	{
		unsigned int v;
		if(!read_int16(v))
			return false;
		dest = v - 0x8000;
		return true;
	}
	
	bool read_sint32(int& dest)
	{
		unsigned int v;
		if(!read_int32(v))
			return false;
		dest = v - 0x80000000;
		return true;
	}

	bool read_string16(std::string& dest)
	{
		unsigned int len;
		if(!read_int16(len)
		|| !read(len))
			return false;
			
		dest.assign((char const*)read_buffer(), len);
		read_consume(len);
		return true;
	}
	
	
	std::size_t consumed()
	{
		return _consumed;
	}
	
	std::size_t _consumed;
};

struct consumption_counter
{
	consumption_counter(istream& istr)
	: istr(istr)
	{
		initial = istr.consumed();
	}
	
	std::size_t consumed()
	{
		return istr.consumed() - initial;
	}
	
	std::size_t left(std::size_t total)
	{
		return total - consumed();
	}
	
private:
	std::size_t initial;
	istream& istr;
};

struct consumption_limiter
{
	consumption_limiter(istream& istr, std::size_t total)
	: istr(istr)
	{
		initial = istr.consumed();
		limit = initial + total;
	}
	
	std::size_t consumed()
	{
		return istr.consumed() - initial;
	}
	
	bool valid()
	{
		return istr.consumed() <= limit;
	}
	
	bool reached()
	{
		return istr.consumed() == limit;
	}
	
	std::size_t left()
	{
		ASSERT(valid());
		return limit - istr.consumed();
	}
	
private:
	std::size_t initial;
	std::size_t limit;
	istream& istr;
};

struct ostream : virtual iobase
{
	void write(byte const* p, size_t size)
	{
		dev->write_amount(p, size);
	}
	
	void write(vl::buffer const& buf)
	{
		dev->write_amount(&buf[0], buf.size());
	}
	
	
	byte* write_buffer(size_t size)
	{
		return dev->write_buffer(size);
	}
	
	bool flush()
	{
		if(dev)
			return dev->flush();
		return true;
	}
	
	bool blocking_flush()
	{
		if(dev)
		{
			while(!dev->flush() && !error())
			{
				dev->blocking(dev);
			}
			
			return dev->write_buf.empty();
		}
		
		return true;
	}
	
	std::size_t unflushed_data()
	{
		return dev->write_buf.size();
	}
	
	ostream& operator<<(char const* str)
	{
		write(reinterpret_cast<byte const*>(str), std::strlen(str));
		return *this;
	}
	
	ostream& operator<<(std::string const& str)
	{
		write(reinterpret_cast<byte const*>(str.data()), str.size());
		return *this;
	}
	
	ostream& operator<<(ostream& (*f)(ostream&))
	{
		return f(*this);
	}
	
	void write_aint(unsigned int v)
	{
		while(true)
		{
			int b = v & 0x7f;
			v >>= 7;
			
			if(v != 0)
				write_int8(b | 0x80);
			else
			{
				write_int8(b);
				break;
			}
		}
	}
	
	void write_int32(unsigned int v)
	{
		byte* dest = write_buffer(4);
		dest[0] = v & 0xFF;
		dest[1] = (v >> 8) & 0xFF;
		dest[2] = (v >> 16) & 0xFF;
		dest[3] = (v >> 24) & 0xFF;
	}
	
	void write_int24(unsigned int v)
	{
		byte* dest = write_buffer(3);
		dest[0] = v & 0xFF;
		dest[1] = (v >> 8) & 0xFF;
		dest[2] = (v >> 16) & 0xFF;
	}
	
	void write_int16(unsigned int v)
	{
		byte* dest = write_buffer(2);
		dest[0] = v & 0xFF;
		dest[1] = (v >> 8) & 0xFF;
	}
	
	void write_int8(unsigned int v)
	{
		byte* dest = write_buffer(1);
		dest[0] = v & 0xFF;
	}
	
	void write_sint16(int v)
	{
		write_int16((unsigned int)(v + 0x8000));
	}
	
	void write_sint32(int v)
	{
		write_int32((unsigned int)(v + 0x80000000));
	}
	
	void write_string16(std::string const& src)
	{
		write_int16(int(src.size()));
		write((vl::byte const*)src.data(), src.size());
	}
};

struct stream : public istream, public ostream
{
	stream()
	: iobase()
	{
	}
	
	stream(device_buf* dev)
	: iobase(dev)
	{
	}
};

struct memorystream : public stream
{
	memorystream()
	: iobase(new memory_buf)
	{
	}
	
	memorystream(std::string const& str)
	: iobase(new memory_buf(reinterpret_cast<byte const*>(str.data()), str.size()))
	{
	}
	
	void clear()
	{
		dev->read_buf.clear();
	}
	
	buffer& internal_buffer() const
	{
		return dev->read_buf;
	}
};

bool extract_line(device_buf* src, std::string& str);

inline bool extract_line(istream& src, std::string& str)
{
	return extract_line(src.get_devbuf(), str);
}

} // namespace vl

#endif // VL_STREAM_HPP

